package cn.skyclass.net
{
	import cn.skyclass.controls.AdvancedAlert;
	import cn.skyclass.controls.WaitingDialog;
	import cn.skyclass.events.EventNotification;
	import cn.skyclass.events.GlobalTipEvent;
	import cn.skyclass.events.RpcEvent;
	import cn.skyclass.utils.Log4Flex;
	
	import flash.events.EventDispatcher;
	
	import mx.managers.CursorManager;
	import mx.rpc.AbstractOperation;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	
	
	/**
	 * 执行远程调用的助手，提供一系列的状态控制和实现本地对象的包装。
	 * 
	 * 共提供三种类型的方法：
	 * 1，从服务器端获取单个对象，例如 getUserInfoById(userId:int):UserBean;
	 * 2，获取一个对象列表，例如 getUserList():ArrayCollection;
	 * 3，执行一个操作，诸如插入对象、更新对象、删除对象等操作，例如 updateUser(bean:UserBean):OperationResult;
	 * 
	 * 远程调用返回结果后，自动将结果集转化为本地对象，然后发布相应的事件。
	 * 
	 * 事件：
	 * RpcEvent.RPC_RESULT : 远程调用结束时将发布此事件，事件对象中包含所有的结果信息。
	 * RpcEvent.RPC_FAULT ：远程调用失败是将发布此事件，事件对象中将包含相应的错误信息。
	 */
	[Event(name="rpcResult", type="cn.skyclass.events.RpcEvent")]
	[Event(name="rpcFault", type="cn.skyclass.events.RpcEvent")]
	public class RpcHelper extends EventDispatcher
	{
		/**
		 * 远程调用的 返回数据类型、方法名称、结果监听函数 以及 调用方法需要的参数
		 */
		protected var resultBeanCla:Class;
		protected var operationName:String;
		protected var operationResultListener:Function;
		protected var operationArgs:Array;
		
	
		/**
		 * 加载数据时的提示信息
		 */
		public var tipMsg:String = null;
		
		/**
		 * 加载数据成功时的提示信息
		 */
		public var tipSuccessfulMsg:String = null;
				
		/**
		 * 加载数据失败时的提示信息
		 */
		public var tipFaildMsg:String = null;
		
		/**
		 * 加载数据失败时的将弹出的提示信息
		 */
		public var alertFaildMsg:String = null;
							
		/**
		 * 是否在控制台输出调用方法的参数信息（参数数据很多时，可适当的将其设为 false）
		 */
		public var traceArgsInfo:Boolean = true;
		
		/**
		 * 是否将错误信息记入日志，向服务器端输出
		 */
		public var logErrorInfo:Boolean = true;
				
		/**
		 * 是否验证参数，主要是对参数进行编码
		 */		
		public var validateArgsInfo:Boolean = true;
		
		/**
		 * 执行调用的时候是否将鼠标置为忙碌状态（时钟图示）
		 */
		public var showBusyCursor:Boolean = true;
		
		/**
		 * 执行调用的时候是否打开用户等待框
		 */
		public var openWaitingDialog:Boolean = false;
		
		/**
		 * 是否使用 AsyncToken 来获取调用结果
		 */
		public var useAsyncToken:Boolean = true;
		
		
		/**
		 * 用于向外发布正在操作的提示信息
		 * 使用默认的 EventNotification
		 */
		public var enInstanceName:String = EventNotification.DEFAULT_INSTANCE_NAME;
		protected function get en():EventNotification{
			return EventNotification.getInstance(this.enInstanceName);
		}
		
		
		/**
		 * 用户等待框，可以强制屏蔽用户的继续操作，即已模态方式弹出对话框
		 */
		protected var waitingDialog:WaitingDialog = WaitingDialog.getInstance();
		
		/**
		 * 远程调用的超时值，秒钟数
		 */
		public var requestTimeoutSeconds:int = 300;
		
		
		/**
		 * 是否正在传输数据
		 */
		private var _transmitting:Boolean = false;		
		[Bindable]
		public function get transmitting():Boolean{
			return this._transmitting;
		}
		public function set transmitting(value:Boolean):void{
			this._transmitting = value;
		}		
			
		
		/**
		 * 加载实例列表，取得的数据存放于 RpcEvent.beansCollection 属性中
		 * 
		 * @operationName : 需要调用的方法名称
		 * @args : 调用方法所需的参数
		 */
		public function loadList(operationName:String, ... args):AsyncToken{
			return callOperation(loadListResultHandler, operationName, args);
		}	
		protected function loadListResultHandler(e:ResultEvent):void{		
			this.endCall(e.target as AbstractOperation);
			trace("get a bean list successfully. result count:");
		}
		
		
		/**
		 * 获取单个的实例，取得数据存放于 RpcEvent.beanData 属性中
		 * 
		 * @operationName : 需要调用的方法名称
		 * @args : 调用方法所需的参数
		 */
		public function getBean(operationName:String, ... args):AsyncToken{
			return callOperation(getBeanResultHandler, operationName, args);
		}		
		protected function getBeanResultHandler(e:ResultEvent):void{		
			this.endCall(e.target as AbstractOperation);				
			trace("get a bean result successfully.");
		}			
		
		
		/**
		 * 执行一项指定的操作，操作结果数据存放于 RpcEvent.result 属性中
		 * 
		 * @operationName : 需要调用的方法名称
		 * @args : 调用方法所需的参数
		 */
		public function execute(operationName:String, ... args):AsyncToken{
			if(this.resultBeanCla == null){
				this.resultBeanCla = OperationResult;
			}			
			return callOperation(executeResultHandler, operationName, args);
		}
		protected function executeResultHandler(e:ResultEvent):void{		
			this.endCall(e.target as AbstractOperation);				
			trace("execute a operation successfully.");
		}			
		
		
		/**
		 * 准备调用远程方法
		 */				
		protected function callOperation(resultListener:Function, 
			operationName:String, args:Array):AsyncToken{
				
			this.operationName = operationName;
			this.operationResultListener = resultListener;
			this.operationArgs = args;
			
			this.startCall();
			return null;
		}
		
		
		/**
		 * 远程调用失败的处理方法
		 */
		protected function faultHandler(e:FaultEvent):void{
			trace("a rpc operation call faild.");
			this.endCall(e.target as AbstractOperation);	
			this.endFaildCall(e.target as AbstractOperation, e);	
		}
		

		/**
		 * 开始远程调用
		 */
		protected function startCall():void{
			this.transmitting = true;
			if(this.tipMsg){
				this.en.dispatchEvent(new GlobalTipEvent(GlobalTipEvent.START_TIP, this.tipMsg));
			}
			if(this.showBusyCursor){
				CursorManager.setBusyCursor();
			}
			if(this.openWaitingDialog){
				var msg:String = this.tipMsg ? this.tipMsg : "";
				this.waitingDialog.beginWaiting(msg);
			}		
			
			trace("");
			if(this.traceArgsInfo){
				trace("--> send a operation, name:", operationName, ", return type:", this.resultBeanCla, 
					", args:", operationArgs);
			}else{
				trace("--> send a operation, name:", operationName, ", return type:", this.resultBeanCla);
			}				
		}
		
		/**
		 * 结束远程调用
		 */
		protected function endCall(opr:AbstractOperation=null):void{
			this.transmitting = false;
			if(this.tipMsg){
				this.en.dispatchEvent(new GlobalTipEvent(GlobalTipEvent.END_TIP, this.tipMsg));
			}			
			if(this.showBusyCursor){
				CursorManager.removeBusyCursor();
			}
			if(this.openWaitingDialog){
				this.waitingDialog.endWaiting();
			}
			
			// remove all possible registered listeners
			if(opr){
				opr.removeEventListener(FaultEvent.FAULT, this.faultHandler);
				opr.removeEventListener(ResultEvent.RESULT, this.loadListResultHandler);
				opr.removeEventListener(ResultEvent.RESULT, this.getBeanResultHandler);
				opr.removeEventListener(ResultEvent.RESULT, this.executeResultHandler);		
			}
		}
		
		/**
		 * 处理调用失败后的结果
		 */
		protected function endFaildCall(opr:AbstractOperation=null, 
			fault:FaultEvent=null, err:Error=null):void{
				
			if(this.tipFaildMsg){
				this.en.dispatchEvent(new GlobalTipEvent(GlobalTipEvent.TEMP_TIP, this.tipFaildMsg));
			}
			if(this.alertFaildMsg){
				AdvancedAlert.error(this.alertFaildMsg);
			}			
			
			var e:RpcEvent = new RpcEvent(RpcEvent.RPC_FAULT);
			e.faultEvent = fault;
			dispatchRpcFaultEvent(e);
			
			// log fault or err to server
			if(this.logErrorInfo && fault){
				new Log4Flex().fault(fault.fault);
			}			
			if(this.logErrorInfo && err){
				new Log4Flex().error(err);
			}			
		}
		
		/**
		 * 处理远程 Execute 的结果
		 */
		protected function endExecuteCall(or:OperationResult):void{
			if(or.successful && this.tipSuccessfulMsg){
				this.en.dispatchEvent(new GlobalTipEvent(GlobalTipEvent.TEMP_TIP, this.tipSuccessfulMsg));
			}
			if(!or.successful && this.tipFaildMsg){
				this.en.dispatchEvent(new GlobalTipEvent(GlobalTipEvent.TEMP_TIP, this.tipFaildMsg));
			}
			if(!or.successful && this.alertFaildMsg){
				AdvancedAlert.error(this.alertFaildMsg);
			}				
		}
		
		
		protected function dispatchRpcResultEvent(e:RpcEvent):void{
			var ee:RpcEvent = new RpcEvent(RpcEvent.RPC_RESULT);
			ee.copyProperties(e);
			dispatchEvent(ee);
		}
		
		protected function dispatchRpcFaultEvent(e:RpcEvent):void{
			var ee:RpcEvent = new RpcEvent(RpcEvent.RPC_FAULT);
			ee.copyProperties(e);
			dispatchEvent(ee);
		}
	}
}
